/*-------------------------------------------------------------------------
 - Copyright (c) 2024-2025 [XD-AMCC TEAM]
 - [XD-AMCC] is licensed under Mulan PSL v2.
 - You can use this software according to the terms and conditions of the Mulan PSL v2.
 - You may obtain a copy of Mulan PSL v2 at:
 -          http://license.coscl.org.cn/MulanPSL2
 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
 - OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 - TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 - See the Mulan PSL v2 for more details.
 ------------------------------------------------------------------------*/


/**
 * @brief Implement the first lexer KERNEL based on DFA.
 *
 * This is a generic DFA Simulator based the transition table of DFA.
 *
 *
 * @author  WXQ#XDU
 * @date    2024.11
 */

#include <stdlib.h>   // for bsearch, qsort

#include "_lex_inner.h"


//  Structure of a transition from a special state.
typedef struct t_transition_of_state {
    char  char_from;   // Lower bound of this char-range
    char  char_to;     // Upper bound of this char-range
    int   state_to;    // Next  state of this transition
} t_transition_of_state;
#define  HAS_TYPE_TRANSITION    1


// Structure of a final state info.
typedef struct t_final_state {
    int            state;      // final state id
    EnumTokenKind  tokenKind;  // token-kind accepted on this state
} t_final_state;
#define  HAS_TYPE_FINALSTATE    1


// all transitions of DFA
#include "../../meta/dfa_trans.inc"   //generated by my tool

// the elements number of allTransitions[], see file "dfa_trans.inc"
static const int N_STATES = sizeof(allTransitions) / sizeof(allTransitions[0]);

// all final states of DFA
#include "../../meta/dfa_finals.inc"  //generated by my tool

// the elements number of finalStates[], see file "dfa_finals.inc"
static const int N_FINALS = sizeof(finalStates) / sizeof(finalStates[0]);


// Get the start state of DFA.
//
static int dfa_get_start_state ( ) {
    return state_init;
}


// The comparison function may be used for
// (1) sort the set of final states,
// (2) find an element from the set of final states.
static int  compare_final (const void * s1, const void * s2) {
    return ((const t_final_state *)s1)->state
         - ((const t_final_state *)s2)->state;
}


// Whether the given state is a final state or not.
// Return value:
//   If the state is final, return the token kind accepted on this state.
//   Otherwise, return TK_ERROR.
//
static EnumTokenKind  dfa_is_final (int state) {
    t_final_state key = {state, TK_ERROR};
    t_final_state * pItem = bsearch (&key,
                            finalStates, N_FINALS,
                            sizeof(t_final_state), compare_final );

    return (NULL == pItem) ? TK_ERROR : pItem->tokenKind ;
}


#define IS_IN_RANGE(ch, min, max) \
    ( ((unsigned char)(min) <= (unsigned char)(ch)) \
    && ((unsigned char)(ch) <= (unsigned char)(max)) )



// Get the next-state for a given char on current state.
// Return value:
//  >=0  the next state
//   <0  no next state for the given char on current state
//
static int dfa_move(int currentState, char  ch) {
    if (currentState < 0 || currentState >= N_STATES) // check state number
        return -1;

    // get the transition table (all transitions from current state),
    // aka, the address of first element of the table
    t_transition_of_state * transOfCurrentState = allTransitions[currentState];
    if (NULL == transOfCurrentState)  // no transition from currentState
        return -1;

    for (bool isInRange = false    ;    ;    ++ transOfCurrentState) {
        // stop loop if this is the end of transition table
        if ( '\0' == transOfCurrentState->char_from ) // see file "dfa_trans.inc"
            break;

        isInRange = IS_IN_RANGE (ch,
                                 transOfCurrentState->char_from,
                                 transOfCurrentState->char_to  );
        if ( isInRange )
            return transOfCurrentState->state_to;
        else
            continue;
    }

    // NOTICE: The above for-loop has poor performance.
    //   You can re-implement it  ----  TRY  IT!

    return -1;
}


t_word_info  dfa_tableDriven_recognize (t_word_info  wordInfo) {
    int  len = 0;
    char ch1 = READ_CHAR (wordInfo.theLexer);
    int  currentState = dfa_get_start_state();

    for (int  nextState  ; ! IS_EOF(ch1)  ;  ) {
        nextState = dfa_move (currentState, ch1);
        if( nextState < 0 ) // no transition from currentState
            break;
        currentState = nextState;
        ++ len;
        ch1 = READ_CHAR (wordInfo.theLexer);
    }

    BACK_CHAR (wordInfo.theLexer); // put back the pre-fetched char
    wordInfo.n_scanedchars = len;
    wordInfo.kind          = dfa_is_final (currentState);
    return wordInfo;
}


// End-Of-File
